package com.blankj.utilcode.util;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.icu.lang.UCharacter;
import android.os.Build;
import android.text.InputFilter;
import android.text.TextUtils;

import androidx.annotation.ArrayRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.IllegalFormatException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * <pre>
 *     author: Blankj
 *     blog  : http://blankj.com
 *     time  : 2016/08/16
 *     desc  : utils about string
 * </pre>
 */
public final class StringUtils {

    private StringUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * Return whether the string is null or 0-length.
     *
     * @param s The string.
     * @return {@code true}: yes<br> {@code false}: no
     */
    public static boolean isEmpty(final CharSequence s) {
        return s == null || s.length() == 0;
    }

    /**
     * Return whether the string is null or whitespace.
     *
     * @param s The string.
     * @return {@code true}: yes<br> {@code false}: no
     */
    public static boolean isTrimEmpty(final String s) {
        return (s == null || s.trim().length() == 0);
    }

    /**
     * Return whether the string is null or white space.
     *
     * @param s The string.
     * @return {@code true}: yes<br> {@code false}: no
     */
    public static boolean isSpace(final String s) {
        if (s == null) {
            return true;
        }
        for (int i = 0, len = s.length(); i < len; ++i) {
            if (!Character.isWhitespace(s.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * Return whether string1 is equals to string2.
     *
     * @param s1 The first string.
     * @param s2 The second string.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public static boolean equals(final CharSequence s1, final CharSequence s2) {
        if (s1 == s2) {
            return true;
        }
        int length;
        if (s1 != null && s2 != null && (length = s1.length()) == s2.length()) {
            if (s1 instanceof String && s2 instanceof String) {
                return s1.equals(s2);
            } else {
                for (int i = 0; i < length; i++) {
                    if (s1.charAt(i) != s2.charAt(i)) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    /**
     * Return whether string1 is equals to string2, ignoring case considerations..
     *
     * @param s1 The first string.
     * @param s2 The second string.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public static boolean equalsIgnoreCase(final String s1, final String s2) {
        return s1 == null ? s2 == null : s1.equalsIgnoreCase(s2);
    }

    /**
     * Return {@code ""} if string equals null.
     *
     * @param s The string.
     * @return {@code ""} if string equals null
     */
    public static String null2Length0(final String s) {
        return s == null ? "" : s;
    }

    /**
     * Return the length of string.
     *
     * @param s The string.
     * @return the length of string
     */
    public static int length(final CharSequence s) {
        return s == null ? 0 : s.length();
    }

    /**
     * Set the first letter of string upper.
     *
     * @param s The string.
     * @return the string with first letter upper.
     */
    public static String upperFirstLetter(final String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
        if (!Character.isLowerCase(s.charAt(0))) {
            return s;
        }
        return (char) (s.charAt(0) - 32) + s.substring(1);
    }

    /**
     * Set the first letter of string lower.
     *
     * @param s The string.
     * @return the string with first letter lower.
     */
    public static String lowerFirstLetter(final String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
        if (!Character.isUpperCase(s.charAt(0))) {
            return s;
        }
        return String.valueOf((char) (s.charAt(0) + 32)) + s.substring(1);
    }

    /**
     * Reverse the string.
     *
     * @param s The string.
     * @return the reverse string.
     */
    public static String reverse(final String s) {
        if (s == null) {
            return "";
        }
        int len = s.length();
        if (len <= 1) {
            return s;
        }
        int mid = len >> 1;
        char[] chars = s.toCharArray();
        char c;
        for (int i = 0; i < mid; ++i) {
            c = chars[i];
            chars[i] = chars[len - i - 1];
            chars[len - i - 1] = c;
        }
        return new String(chars);
    }

    /**
     * Convert string to DBC.
     *
     * @param s The string.
     * @return the DBC string
     */
    public static String toDBC(final String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
        char[] chars = s.toCharArray();
        for (int i = 0, len = chars.length; i < len; i++) {
            if (chars[i] == 12288) {
                chars[i] = ' ';
            } else if (65281 <= chars[i] && chars[i] <= 65374) {
                chars[i] = (char) (chars[i] - 65248);
            } else {
                chars[i] = chars[i];
            }
        }
        return new String(chars);
    }

    /**
     * Convert string to SBC.
     *
     * @param s The string.
     * @return the SBC string
     */
    public static String toSBC(final String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
        char[] chars = s.toCharArray();
        for (int i = 0, len = chars.length; i < len; i++) {
            if (chars[i] == ' ') {
                chars[i] = (char) 12288;
            } else if (33 <= chars[i] && chars[i] <= 126) {
                chars[i] = (char) (chars[i] + 65248);
            } else {
                chars[i] = chars[i];
            }
        }
        return new String(chars);
    }

    /**
     * Return the string value associated with a particular resource ID.
     *
     * @param id The desired resource identifier.
     * @return the string value associated with a particular resource ID.
     */
    public static String getString(@StringRes int id) {
        return getString(id, (Object[]) null);
    }

    /**
     * Return the string value associated with a particular resource ID.
     *
     * @param id         The desired resource identifier.
     * @param formatArgs The format arguments that will be used for substitution.
     * @return the string value associated with a particular resource ID.
     */
    public static String getString(@StringRes int id, Object... formatArgs) {
        try {
            return format(Utils.getApp().getString(id), formatArgs);
        } catch (Resources.NotFoundException e) {
            e.printStackTrace();
            return String.valueOf(id);
        }
    }

    /**
     * Return the string array associated with a particular resource ID.
     *
     * @param id The desired resource identifier.
     * @return The string array associated with the resource.
     */
    public static String[] getStringArray(@ArrayRes int id) {
        try {
            return Utils.getApp().getResources().getStringArray(id);
        } catch (Resources.NotFoundException e) {
            e.printStackTrace();
            return new String[]{String.valueOf(id)};
        }
    }

    /**
     * Format the string.
     *
     * @param str  The string.
     * @param args The args.
     * @return a formatted string.
     */
    public static String format(@Nullable String str, Object... args) {
        String text = str;
        if (text != null) {
            if (args != null && args.length > 0) {
                try {
                    text = String.format(str, args);
                } catch (IllegalFormatException e) {
                    e.printStackTrace();
                }
            }
        }
        return text;
    }

    /**
     * utf-8 转换成 unicode
     *
     * @param inStr
     * @return
     * @author fanhui
     * 2007-3-15
     */
    public static String utf8ToUnicode(String inStr) {
        char[] myBuffer = inStr.toCharArray();

        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < inStr.length(); i++) {
            @SuppressLint({"NewApi", "LocalSuppress"}) UCharacter.UnicodeBlock ub =
                    UCharacter.UnicodeBlock.of(myBuffer[i]);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                if (ub == UCharacter.UnicodeBlock.BASIC_LATIN) {
                    //英文及数字等
                    sb.append(myBuffer[i]);
                } else if (ub == UCharacter.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
                    //全角半角字符
                    int j = (int) myBuffer[i] - 65248;
                    sb.append((char) j);
                } else {
                    //汉字
                    short s = (short) myBuffer[i];
                    String hexS = Integer.toHexString(s);
                    String unicode = "\\u" + hexS;
                    sb.append(unicode.toLowerCase());
                }
            }
        }
        return sb.toString();
    }

    /**
     * unicode 转换成 utf-8
     *
     * @param theString
     * @return
     * @author fanhui
     * 2007-3-15
     */
    public static String unicodeToUtf8(String theString) {
        char aChar;
        int len = theString.length();
        StringBuffer outBuffer = new StringBuffer(len);
        for (int x = 0; x < len; ) {
            aChar = theString.charAt(x++);
            if (aChar == '\\') {
                aChar = theString.charAt(x++);
                if (aChar == 'u') {
                    // Read the xxxx
                    int value = 0;
                    for (int i = 0; i < 4; i++) {
                        aChar = theString.charAt(x++);
                        switch (aChar) {
                            case '0':
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                            case '8':
                            case '9':
                                value = (value << 4) + aChar - '0';
                                break;
                            case 'a':
                            case 'b':
                            case 'c':
                            case 'd':
                            case 'e':
                            case 'f':
                                value = (value << 4) + 10 + aChar - 'a';
                                break;
                            case 'A':
                            case 'B':
                            case 'C':
                            case 'D':
                            case 'E':
                            case 'F':
                                value = (value << 4) + 10 + aChar - 'A';
                                break;
                            default:
                                throw new IllegalArgumentException(
                                        "Malformed   \\uxxxx   encoding.");
                        }
                    }
                    outBuffer.append((char) value);
                } else {
                    if (aChar == 't') {
                        aChar = '\t';
                    } else if (aChar == 'r') {
                        aChar = '\r';
                    } else if (aChar == 'n') {
                        aChar = '\n';
                    } else if (aChar == 'f') {
                        aChar = '\f';
                    }
                    outBuffer.append(aChar);
                }
            } else {
                outBuffer.append(aChar);
            }
        }
        return outBuffer.toString();
    }

    /**
     * 过滤中文
     *
     * @param str
     * @return
     */
    public static String filterChinese(String str) {
        return str.replaceAll("[^(\\u4e00-\\u9fa5)]", "");
    }

    /**
     * 获取中文
     *
     * @param str
     * @return
     */
    public static String getChinese(String str) {
        String regEx = "[^(\\u4e00-\\u9fa5)]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(str);
        return m.replaceAll("").trim();
    }

    /**
     * 获取数字
     *
     * @param str
     * @return
     */
    public static String getNumber(String str) {
        String regEx = "[0-9.,]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(str);
//        return m.replaceAll("").trim();
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            sb.append(m.group());
        }
        return sb.toString();
    }

    /**
     * 获取金额
     *
     * @param str
     * @return
     */
    public static String getPrice(String str) {
        String regEx = "[+-]?[0-9,]+[.]?[0-9]*";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            sb.append(m.group());
        }
        return sb.toString();
    }

    /**
     * 格式化金额
     *
     * @param price   金额
     * @param pattern 格式
     * @return
     */
    public static String formatPrice(String price, String pattern) {
        if (!TextUtils.isEmpty(price) && !TextUtils.isEmpty(pattern)) {
            try {
                BigDecimal decimal = new BigDecimal(price);
                return new DecimalFormat(pattern).format(decimal);
            } catch (NumberFormatException e) {
                return "";
            }
        }
        return "";
    }

    /**
     * 格式化积分
     *
     * @param integral 积分
     * @return
     */
    public static String formatIntegral(int integral) {
        //",###,###" 没有小数
        //",###,###.00" 两位小数
        return formatPrice(String.valueOf(integral), ",###,###");
    }

    /**
     * 是否为8~16位密码
     *
     * @param str
     * @return
     */
    public static boolean isPassword(String str) {
        String regex = "^(?![^a-zA-Z]+$)(?!\\D+$).{8,16}$";
        return str.matches(regex);
    }

    /**
     * method is used for checking valid email id format.
     *
     * @param email
     * @return boolean true for valid false for invalid
     */
    public static boolean isEmailValid(String email) {
        boolean isValid = false;

        String expression = "^[\\w\\.-]+@([\\w\\-]+\\.)+[A-Z]{2,4}$";
        CharSequence inputStr = email;

        Pattern pattern = Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(inputStr);
        if (matcher.matches()) {
            isValid = true;
        }
        return isValid;
    }

    /**
     * 格式化银行卡显示效果
     *
     * @param no
     * @return
     */
    public static String processCardNumber(String no) {
        if (!TextUtils.isEmpty(no)) {
            no = no.replace(" ", "");
            StringBuilder stringBuilder = new StringBuilder(no);
            int afterLength = no.length() / 4 + no.length();
            for (int i = 0; i < afterLength; i++) {
                if ((i + 1) % 5 == 0) {
                    stringBuilder.insert(i, " ");
                }
            }
            return stringBuilder.toString();
        }
        return no;
    }

    /**
     * 中英文和数字检查
     */
    public static boolean isValidString(String str) throws PatternSyntaxException {
        // 只允许字母、数字和汉字
        String regEx = "[^a-zA-Z0-9\u4E00-\u9FA5]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(str);
        String res = m.replaceAll("").trim();
        return res.equals(str);
    }

    /**
     * OSS缩略图url配置
     *
     * @param context
     * @param sourceUrl
     * @return
     */
    public static String genOSSImageUrl(Context context, String sourceUrl) {
        if (TextUtils.isEmpty(sourceUrl)) {
            return null;
        }
        return sourceUrl + "?x-oss-process=image/resize,w_500/quality,q_90";
    }

    /**
     * 数字转换形式（适用投票和点赞等）
     *
     * @param num
     * @return
     */
    public static String digitalConversion(int num) {
        String OriginallyNum = String.valueOf(num);
        String result = "";
        if (num >= 10000) {
            int count = LengthNum(num);
            switch (count) {
                case 5:
                    result = OriginallyNum.substring(0, 1) + "." + OriginallyNum.substring(1, 2) + "w";
                    break;
                case 6:
                    result = OriginallyNum.substring(0, 2) + "." + OriginallyNum.substring(2, 3) + "w";
                    break;
                case 7:
                    result = OriginallyNum.substring(0, 3) + "." + OriginallyNum.substring(3, 4) + "w";
                    break;
                default:
                    result = OriginallyNum.substring(0, 4) + "." + OriginallyNum.substring(4, 5) + "w";
                    break;
            }
        } else {
            result = num + "";
        }

        return result;
    }

    /**
     * 获取数字长度（几位数）
     *
     * @param num
     * @return
     */
    public static int LengthNum(int num) {
        int numNew = num;
        int count = 0; //计数
        while (numNew >= 1) {
            numNew /= 10;
            count++;
        }
        return count;
    }

    /**
     * 解析缩放比例
     *
     * @param scale
     * @return
     */
    public static float[] parseScale(String scale) {
        float scaleWidth = 1f;
        float scaleHeight = 1f;
        if (!TextUtils.isEmpty(scale)) {
            String[] splitScale = scale.split(":");
            try {
                scaleWidth = Float.parseFloat(splitScale[0]);
                scaleHeight = Float.parseFloat(splitScale[1]);
            } catch (NumberFormatException e) {
            }
        }

        return new float[]{scaleWidth, scaleHeight};
    }

    /**
     * 隐藏手机中间4位
     *
     * @param phoneNumber
     * @return
     */
    public static String desensitizedPhoneNumber(String phoneNumber) {
        if (!TextUtils.isEmpty(phoneNumber)) {
            return phoneNumber.replaceAll("(\\w{3})\\w*(\\w{4})", "$1****$2");
        } else {
            return "";
        }
    }

    /**
     * 获取字数长度过滤器
     *
     * @param maxLen
     * @return
     */
    public static InputFilter getLenFilter(int maxLen) {
        //editText.setFilters(new InputFilter[]{getLenFilter(20)});
        return (source, start, end, dest, dStart, dEnd) -> {
            int dindex = 0;
            int count = 0;

            while (count <= maxLen && dindex < dest.length()) {
                char c = dest.charAt(dindex++);
                if (c < 128) {
                    count = count + 1;
                } else {
                    count = count + 2;
                }
            }

            if (count > maxLen) {
                return dest.subSequence(0, dindex - 1);
            }

            int sindex = 0;
            while (count <= maxLen && sindex < source.length()) {
                char c = source.charAt(sindex++);
                if (c < 128) {
                    count = count + 1;
                } else {
                    count = count + 2;
                }
            }

            if (count > maxLen) {
                sindex--;
            }

            return source.subSequence(0, sindex);
        };
    }
}
